package com.paypal.utils.cb.kafka;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.naming.ConfigurationException;
import org.apache.commons.codec.binary.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.couchbase.client.TapClient;
import net.spy.memcached.tapmessage.ResponseMessage;
import net.spy.memcached.tapmessage.TapStream;
/**
* CBMessage Consumer knows how to connect to CB and extract data.
* It uses TAP client to incrementally fetch new messages from Couchbase.
* @author ssudhakaran
*
*/
public class CBMessageConsumer {
/**
* Logger
*/
private static final Logger LOGGER = LoggerFactory.getLogger(CBMessageConsumer.class);
/**
* Couchbase TAP client used to read messages from Couchbase.
*/
private static TapClient tapClient;
/**
* Date from which messages should be read from couchbase.
*/
private static final long STARTDATE=Long.valueOf(ConfigLoader.getProp(Constants.STARTDATE));
/**
* Couchbase connect URI
*/
private transient final List<URI> CBURI;
/**
* Couchbase bucket info
*/
private final String bucket;
/**
* Named stream to fetch data from couchbase.
*/
private final String streamname;
/**
* Full dump from Coucbase?
*/
private boolean fullDump=false;
/**
* Inititalize with connection parameters
* @param uri
* @param bucket
* @param password
*/
public CBMessageConsumer(){
this.CBURI = new ArrayList<URI>();
String servername=ConfigLoader.getProp(Constants.CBSERVER);
this.CBURI.add(URI.create(servername));
this.bucket=ConfigLoader.getProp(Constants.BUCKET);
this.streamname=ConfigLoader.getProp(Constants.STREAMNAME);
this.fullDump=Boolean.parseBoolean(ConfigLoader.getProp(Constants.ISFULLDUMP));
//initTapClient();
}
/**
* Overloaded constructor to pass fulldump variable.
* @param host
* @param bucket
* @param fulldump
*/
public CBMessageConsumer(String host,String bucket,boolean fulldump){
this.CBURI = new ArrayList<URI>();
String servername=host;
this.CBURI.add(URI.create(servername));
this.bucket=bucket;
this.streamname=ConfigLoader.getProp(Constants.STREAMNAME);
this.fullDump=fulldump;
//initTapClient();
}
/**
* Init TAP Client.
*/
private void initTapClient(){
if(LOGGER.isInfoEnabled()){
LOGGER.info("INIT "+CBURI.get(0).getHost() +", bucket :"+bucket+", streamname:"+streamname+",STARTDATE:"+STARTDATE);
}
//Create TAP Client.
tapClient=new TapClient(CBURI, bucket, "");
try {
if(LOGGER.isInfoEnabled()){
LOGGER.info("initTapClient : start date"+STARTDATE);}
//If we need full dump, get everything.
if(fullDump){
tapClient.tapDump(streamname);
}else{
//Get message starting from date STARTDATE
tapClient.tapBackfill(null,-1,0, TimeUnit.MINUTES);
//tapClient.tapBackfill(null,STARTDATE,0, TimeUnit.MINUTES);
}
} catch (ConfigurationException e) {
if(LOGGER.isErrorEnabled()){ LOGGER.error(e.getExplanation()+e.getMessage());}
tapClient=null;
} catch (IOException e) {
if(LOGGER.isErrorEnabled()){ LOGGER.error(e.getMessage());}
tapClient=null;
}
}
/**
* read messages from CB TAP
* @return
* @throws IOException
*/
public void run() {
LOGGER.info("RUNNING Couchbase Consumer");
//If TAP Client is not running.
if(tapClient==null){
try{
initTapClient();
}catch(com.couchbase.client.vbucket.ConfigurationException e){
tapClient=null;
if(LOGGER.isErrorEnabled()){ LOGGER.error("Not able to connect to Couchbase. Will retry in "+ConfigLoader.getProp(Constants.INTERVAL_SEC,Constants.INTERVAL_SEC_DEF)+" seconds.");}
return;
}
}
//If a valid Kafka producer doesn't exist. Try after 2 minutes
if(!CBKafkaProducer.isValidProducer()){
LOGGER.info("No Kafka Connection. Retry after "+ConfigLoader.getProp(Constants.INTERVAL_SEC,Constants.INTERVAL_SEC_DEF)+" seconds.");
return;
}
int iReadCounter=0;
try{
//Keep reading from Tap Client
while(tapClient.hasMoreMessages()){
//Read message from TAP client
final ResponseMessage resmessage=tapClient.getNextMessage();
if(resmessage!=null ) {
if(resmessage.getValue()!=null){
iReadCounter++;
//Publish message to Kafka.
CBKafkaProducer.publishMessage(resmessage.getKey(),StringUtils.newStringUtf8(resmessage.getValue()));
if(iReadCounter%3==0){
iReadCounter=0;
if(LOGGER.isInfoEnabled()) LOGGER.info("TIME :"+new java.util.Date().getTime());
}
}
}
}
}catch(Exception e){
e.printStackTrace();
if(LOGGER.isErrorEnabled()) LOGGER.error("EXCEPTION. TIME :"+new java.util.Date().getTime());
}
}
public static void main(String[] args) throws IOException{
CBMessageConsumer cbConsumer=new CBMessageConsumer();
cbConsumer.run();
//ScheduledExecutorService fScheduler=Executors.newScheduledThreadPool(Constants.NUM_THREADS);
//ScheduledFuture<?> cbConsumerFuture = fScheduler.scheduleWithFixedDelay(cbConsumer, Integer.valueOf(ConfigLoader.getProp(Constants.START_DELAY_SEC,"0")), Integer.valueOf(ConfigLoader.getProp(Constants.INTERVAL_SEC,Constants.INTERVAL_SEC_DEF)), TimeUnit.SECONDS);
}
}